#!/usr/bin/python
# -*- coding: utf-8 -*-

__all__ = [u'GuiCollectionEditor']


import sys
from PyQt4 import QtGui
from PyQt4.QtCore import (SIGNAL, Qt)


from _pylot.qtgui.interfaces.widgetinterface import WidgetInterface
from _pylot.qtgui.widgets.gui_typedchoosers import (GuiBoolChooser,
  GuiFloatChooser, GuiIntChooser, GuiPathChooser, GuiUnicodeChooser, CHOOSERS)
from _pylot.qtgui.styles.policy import *

from pylot.core import (Datum, Path, DirPath, FilePath, datum_name,
                                Collection, Book, GroupBook, check_type,
                                Configuration, good_level, ConfigOption,
                                uprint)

class GuiCollectionEditor(QtGui.QScrollArea, WidgetInterface):
  u"""
  Creates a QWidget for editing :class:`~pylot.core.Collection`,
  :class:`~pylot.core.Book` or :class:`~pylot.core.Datum`

  ** SIGNALS **
    
  - SIGNAL datumWithId???Changed() : this signal is sent 
    when field corresponding to datum with id ??? is changed. 
    It uses the hexadecimal format of datum id. You can get this id using 
    :obj:`id` and :obj:`hex` functions on a datum instance.
    
    Example :

    >>> datum = Datum()
    >>> datum_id = hex(id(datum))
    
    For example, if datum_id == 0x9cf02ac, you can connect signal
    datumWithId0x9cf02acChanged.
    
    This signal is useful to track a particular field without worry about
    internal GuiCollectionEditor implementation.

  - SIGNAL dataChanged [datum, data_widget] : this signal is sent when a value in
    one field of GuiCollectionEditor change. 
    datum is the corresponding Datum (datum used to generate the field)
    data_widget is the QWidget containing the value. For example for Datum with
    nature=int, data_widget is QSpinBox.

  Example of use:

  >>> from PyQt4.QtGui import QApplication
  >>> app = QApplication([])

  >>> from pylot.core import Collection
  >>> collection = Collection()
  >>> collection.upFromFile(u'pyLotTests/doctests/data/config.csv')  
  
  >>> editor = GuiCollectionEditor(collection)
  >>> editor.updateData()
  >>> editor.show()
  
  >>> ret = app.exec_()
  
  Adding or change choosers
  
  You can add chooser to support new types or change default chooser with
  choosers attribute.
  This attribute is a dictionary.
  key is the type (int for example) and value is the associated chooser 
  (GuiIntChooser for instance). You can add keys or change values but values
  must be classes derivated from :class:`~pylot.qtgui.GuiTypedChooser`
  
  Default choosers are :

    - bool: :class:`~pylot.qtgui.GuiBoolChooser`
    - int: :class:`~pylot.qtgui.GuiIntChooser`
    - float: :class:`~pylot.qtgui.GuiFloatChooser`
    - unicode: :class:`~pylot.qtgui.GuiUnicodeChooser`
    - Path: :class:`~pylot.qtgui.GuiPathChooser`
    - DirPath: :class:`~pylot.qtgui.GuiPathChooser`
    - FilePath: :class:`~pylot.qtgui.GuiPathChooser`

  """
  def __init__(self, data_obj, book_list=None, handler=None,
               groupbook=GroupBook()):
    u"""
    :param data_obj: the collection/book/datum you want to edit
    :param book_list: book name list. If defined, only books in this list are displayed
    :type book_list: list or None
    """
    QtGui.QScrollArea.__init__(self)
    WidgetInterface.__init__(self, handler)

    self.setFrameShape(QtGui.QFrame.NoFrame)
    self.setWidgetResizable(True)
    self.setSizePolicy(QSizePolicy(MinimumExpanding, MinimumExpanding))

    self._choosers = CHOOSERS

    self._data_obj = data_obj
    self._book_list = book_list
    self._groupbook = groupbook

    self._status_widget = QtGui.QLabel()
    self._refresh()
    self._createConnexions()

  choosers = property(fget=lambda self:self._choosers)

  def refresh(self):
    # disable recursive refresh because choosers are destroyed and created again
    self._refresh()

  def _refresh(self):
    self._widgets = [] #clear child widgets
    self._dict_datum_chooser = {} # key: datum, value: corresponding chooser obj
    self._dict_chooser_datum = {} # reversed dict
    self._qt_group_boxes = [] #list of group boxes
    self._qt_book_group_boxes = []

    self._status_widget = QtGui.QLabel()

    self.content = QtGui.QWidget()
    self.content.setSizePolicy(QSizePolicy(MinimumExpanding, MinimumExpanding))
    self.content_layout = QtGui.QVBoxLayout(self.content)
    self.setWidget(self.content)

    if issubclass(type(self._data_obj), Collection) :
      self.main_widget = self._createCollectionEditorWidget(self._data_obj, self._book_list)
      self.content_layout.addWidget(self.main_widget)

    elif issubclass(type(self._data_obj), Book) :
      self.main_widget = self._createBookEditorWidget(self._data_obj, self._groupbook)
      self.content_layout.addWidget(self.main_widget)

    elif issubclass(type(self._data_obj), Datum) :
      self.main_widget = self._createDatumEditorWidget(self._data_obj, u'default')
      if self.main_widget :
        self.content_layout.addWidget(self.main_widget)
    else :
      check_type(u'data_obj', self._data_obj, [Datum, Book, Collection])
    self.content_layout.addWidget(self._status_widget)
    self.content_layout.addStretch()
    self.switchUserLevel()

  def switchUserLevel(self, level=u'user'):
    # disable recursive behavior
    self._switchUserLevel(level)

  def _switchUserLevel(self, level):
    u"""
    :param level: Display level. Options with level lesser or equal than level parameter are displayed
    :type level: unicode
    """
    for groupbox in self._qt_group_boxes :
      is_empty = True
      for child in groupbox.children() :
        try: datum = self._dict_chooser_datum[child]
        except KeyError: pass
        else:
          try : level = datum.level
          except AttributeError :
            child.show()
            is_empty = False
          else :
            if self.handler.goodLevel(level):
              child.show()
              is_empty = False
            else :
              child.hide()
      if is_empty :
        groupbox.hide()
        groupbox.is_empty = True
      else :
        groupbox.show()
        groupbox.is_empty = False

      all_empty = True
      for groupbox in self._qt_book_group_boxes :
        is_empty = True
        for child in groupbox.children():
          if isinstance(child, QtGui.QWidget):
            try : empty = child.is_empty
            except AttributeError :
              is_empty = False
              break
            else :
              if empty is False :
                is_empty = False
                break
        if is_empty :
          groupbox.hide()
        else :
          all_empty = False
          groupbox.show()
        if all_empty :
          self._status_widget.setText(tr_(u'No options at this level'))
          self._status_widget.show()
        else :
          self._status_widget.hide()

  def _translate(self):
    if self._data_obj.alias :
      title = self._data_obj.alias
    elif self._data_obj.name :
      title = self._data_obj.name
    else :
      title = tr_(u'Anonymous')
    self._setTexts([
      (self.main_widget, title),
      (self, title)
      ])

    for groupbox in self._qt_group_boxes :
      title = unicode(groupbox.title())
      if title :
        groupbox.setTitle(self.tr_(title))

  def _createConnexions(self):
    pass

  def _emitDataChanged(self, value):
    datum = self.sender().getDatum()
    self.emit(SIGNAL("dataChanged"), datum, value)
    self.emit(SIGNAL("datumWithId%sChanged()" % hex(id(datum))))

  def _groupBox (self, groupname, book, groupbook):
    alias, tooltip = groupbook.getMetaInfos(groupname)
    groupbox = QtGui.QGroupBox(self)
    self._qt_group_boxes.append(groupbox)
    groupbox.setFlat(True)
    groupbox.setTitle(alias)
    if tooltip:
      groupbox.setToolTip(tooltip)

    if self.handler.goodLevel(u'expert') :
      whatsthis = tr_(u'<p>groupbox alias: %s<br/>') % alias
      whatsthis += tr_(u'book id: %s</p>') % book.name
      groupbox.setWhatsThis(whatsthis)

    vertical_layout = QtGui.QVBoxLayout(groupbox)
    vertical_layout.setObjectName(u"verticalLayout_%s" % groupname)

    datum_list = book.extract(group=groupname)
    for datum_id in datum_list:
      datum = book[datum_id]
      widget = self._createDatumEditorWidget(datum, book.name)
      if widget :
        vertical_layout.addWidget(widget)
    return groupbox

  def _createCollectionEditorWidget (self, collection, book_list=[]):
    # _create usable book list and 'groupbook' object
    # groupbook: book containing aliases and tooltips for groups
    if book_list :
      books = book_list
    else :
      books = collection.keys()
      if u'groupbook' in books :
        books.remove(u'groupbook')

    if u'groupbook' in collection :
      groupbook = collection[u'groupbook']
    else :
      groupbook = GroupBook()

    if collection.alias :
      alias = collection.alias
    else :
      alias = collection.name
    widget = QtGui.QGroupBox(alias)
    widget.setFlat(True)

    if self.handler.goodLevel(u'expert') :
      whatsthis = tr_(u'<p>groupbox alias: %s<br/>') % alias
      whatsthis += tr_(u'collection id: %s</p>') % collection.name
      widget.setWhatsThis(whatsthis)

    tab_books = QtGui.QVBoxLayout(widget)

    for book_id in books :
      book = collection[book_id]
      book_tab = self._createBookEditorWidget(book, groupbook)
      tab_books.addWidget(book_tab)
      line = QtGui.QFrame()
      line.setFrameShape(QtGui.QFrame.VLine)
      line.setFrameShadow(QtGui.QFrame.Sunken)
      tab_books.addWidget(line)

    return widget

  def _createBookEditorWidget (self, book, groupbook):
    u"""
    Creates a Book editor widget.
    Splits book using datum groups informations
    """
    if book.alias :
      name = book.alias
    else :
      name = book.name

    book_tab = QtGui.QGroupBox(name)
    book_tab.setFlat(True)
    book_tab_layout = QtGui.QVBoxLayout(book_tab)
    self._qt_book_group_boxes.append(book_tab)

    for groupname in book.groupDict() :
      if groupname != u'group' :
        groupbox = self._groupBox(groupname, book, groupbook)
        book_tab_layout.addWidget(groupbox)

    return book_tab

  def _createDatumEditorWidget (self, datum, book_id):
    u"""
    Creates a GuiTypedChooser widget from datum and adds it to self._dict_datum_chooser
    """
    if datum.nature in self._choosers.keys() :
      widget = self._choosers[datum.nature](datum, book_id, handler=self.handler)
      self.connect(widget, SIGNAL('dataChanged'),
                   self._emitDataChanged)
      self._dict_datum_chooser[datum] = widget
      self._dict_chooser_datum[widget] = datum
      self.addpyLotWidget(widget)
      return widget
    else :
      uprint(tr_(u'%sChooser not implemented') % datum.nature.__name__, u'\n',
             file=sys.stderr)
      return None

  def resetDataToDefault(self):
    for widget in self._dict_datum_chooser.values():
      widget.resetDataToDefault()

if __name__ == u'__main__' :
  from PyQt4.QtGui import QApplication
  from pylot.core import hardbook
  app = QApplication([])

  collec = Configuration()
  collec.upFromFile(hardbook(u'pylotsrc', u'pyLotTests/doctests/data/config.csv'))
#  editor = GuiCollectionEditor(collec, title=u'Collection Editor')
#  editor = GuiCollectionEditor(collec[u'livre1'])
  editor = GuiCollectionEditor(collec)
#  editor.updateData()
  editor.show()

  ret = app.exec_()

  editor.updateData()
  uprint(u'Following data must be identical to Gui View Data')
  uprint(collec[u'livre1'].infos)

#  import doctest
#  doctest.testmod()
